package com.katesoft.scale4j.rttp.spring.postprocessor;

import static com.katesoft.scale4j.common.spring.IPostProcessingOrder.RTTP_HIBERNATE_POST_PROCESSOR;

import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.event.SaveOrUpdateEvent;
import org.hibernate.event.SaveOrUpdateEventListener;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

import com.katesoft.scale4j.log.LogFactory;
import com.katesoft.scale4j.log.Logger;
import com.katesoft.scale4j.persistent.hibernate.EventListeners;
import com.katesoft.scale4j.persistent.model.RevisionDomainEntity;
import com.katesoft.scale4j.persistent.model.unified.AbstractPersistentEntity;
import com.katesoft.scale4j.persistent.spring.postprocessor.HibernateSessionExtensionPostProcessor;
import com.katesoft.scale4j.rttp.hibernate.ClusteredIncrementGenerator;
import com.katesoft.scale4j.rttp.subscription.CloudSubscriber;

/**
 * This is internal class(will add hibernate event listener for assigning entities id)
 * 
 * @author kate2007
 */
public class RttpHibernatePostProcessor extends HibernateSessionExtensionPostProcessor {
   private transient Logger logger = LogFactory.getLogger(getClass());
   private transient ClusteredIncrementGenerator idGenerator;
   //
   private Boolean generateSchemaUpdateScript;
   private Boolean generateSchemaCreateScript;
   private Boolean generateSchemaDropScript;

   public RttpHibernatePostProcessor() {
      setOrder(RTTP_HIBERNATE_POST_PROCESSOR);
   }

   @Override
   public void postProcessBeanFactory(
            ConfigurableListableBeanFactory configurableListableBeanFactory) {
      if (beanProperties == null) {
         beanProperties = new Properties();
      }
      final CloudSubscriber cloudSubscriber = idGenerator.getBridge().getCloudSubscriber();
      //
      Map<String, Object> listeners = new HashMap<String, Object>() {
         private static final long serialVersionUID = -6091696672790308642L;

         {
            Collection<?> c = Collections.singleton(new SaveOrUpdateEventListener() {
               private static final long serialVersionUID = 4350846494984910812L;

               @Override
               public void onSaveOrUpdate(SaveOrUpdateEvent event) throws HibernateException {
                  if (event.getObject() instanceof AbstractPersistentEntity) {
                     AbstractPersistentEntity bo = (AbstractPersistentEntity) event.getObject();
                     if (bo.getUniqueIdentifier() == null) {
                        long uniqueIdentifier = idGenerator.generate(bo);
                        logger.debug("identifier %s generated for IBO %s", uniqueIdentifier, bo
                                 .getPersistentClass().getSimpleName());
                        bo.setUniqueIdentifier(uniqueIdentifier);
                     }
                  } else if (event.getObject() instanceof RevisionDomainEntity) {
                     RevisionDomainEntity entity = (RevisionDomainEntity) event.getObject();
                     if (entity.getUniqueIdentifier() == null) {
                        long uniqueIdentifier = idGenerator.generate(entity);
                        logger.debug("revision %s generated for %s", uniqueIdentifier, entity
                                 .getClass().getSimpleName());
                        entity.setUniqueIdentifier(uniqueIdentifier);
                     }
                  }
               }
            });
            put(EventListeners.POST_INSERT, Collections.singleton(cloudSubscriber));
            put(EventListeners.POST_UPDATE, Collections.singleton(cloudSubscriber));
            put(EventListeners.POST_DELETE, Collections.singleton(cloudSubscriber));
            put(EventListeners.SAVE, c);
            put(EventListeners.SAVE_UPDATE, c);
         }
      };
      beanProperties.put("eventListeners", listeners);
      //
      if (generateSchemaUpdateScript != null) {
         beanProperties.put("generateSchemaUpdateScript", generateSchemaUpdateScript);
      }
      if (generateSchemaCreateScript != null) {
         beanProperties.put("generateSchemaCreateScript", generateSchemaCreateScript);
      }
      if (generateSchemaDropScript != null) {
         beanProperties.put("generateSchemaDropScript", generateSchemaDropScript);
      }
      //
      super.postProcessBeanFactory(configurableListableBeanFactory);
      logger.debug("cloud subscription %s was added", cloudSubscriber);
   }

   /**
    * set cluster aware id generator.
    * 
    * @param idGenerator
    *           cluster aware id generator.
    */
   @Required
   public void setIdGenerator(ClusteredIncrementGenerator idGenerator) {
      this.idGenerator = idGenerator;
   }

   public void setGenerateSchemaUpdateScript(boolean generateSchemaUpdateScript) {
      this.generateSchemaUpdateScript = generateSchemaUpdateScript;
   }

   public void setGenerateSchemaCreateScript(boolean generateSchemaCreateScript) {
      this.generateSchemaCreateScript = generateSchemaCreateScript;
   }

   public void setGenerateSchemaDropScript(boolean generateSchemaDropScript) {
      this.generateSchemaDropScript = generateSchemaDropScript;
   }
}
